<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */

namespace App\Service;


use App\Job\JPushSingleChatJob;
use App\Job\PushGlobalNotificationJob;
use App\Log\WsLog;
use App\Model\Admin;
use App\Model\ChatMsg;
use App\Model\ChatUser;
use App\Model\ChatUserGroup;
use App\Model\User;
use Hyperf\Contract\OnCloseInterface;
use Hyperf\Contract\OnMessageInterface;
use Hyperf\Contract\OnOpenInterface;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Utils\ApplicationContext;
use Hyperf\WebSocketServer\Context;

class WebSocketController implements OnMessageInterface, OnOpenInterface, OnCloseInterface
{
    /**
     * @var QueueService
     */
    #[Inject]
    protected $queueServer;

    public const WS_TYPE_USER = 1; // 用户

    public const WS_TYPE_CUSTOMER_USER = 2; // 客服

    public const REDIS_CUSTOMER_KEY = 'Remit_WebSocketController_fd_with_customer_id_';
    public const REDIS_CUSTOMER_FD_KEY = 'Remit_WebSocketController_customer_id_with_fd_';

    public const SUCCESS = '0_SUCCESS';

    public const USER_EMPTY = '10005_用户不存在';

    public const TOKEN_ERROR = '10100_token验证失败';

    public function onMessage($server, $frame): void
    {
        // 解析传过来的参数
        $data = json_decode($frame->data, true);

        // 是不是心跳包
        if (isset($data['type']) && $data['type'] == 'Ping') {
            $server->push($frame->fd, 'Ping'); // 心跳
        } else {
            // 根据token 获取当前fd 对应的用户信息
            $token = json_decode(Context::get('token'), true);

            if ($token['type'] == self::WS_TYPE_USER) {
                // 普通用户
                $user = User::find($token['id']);
                $params = [
                    'type' => $data['type'],
                    'content' => $data['content'],
                    'img_path' => $data['img_path'],
                    'uid' => $user->id,
                ];
                $chat = ChatUserGroup::create($params);

                $chatSource = ChatUserGroup::with('user')->with('customer')->find($chat->id);

                if ($server->exist($frame->fd)) {
                    $server->push($frame->fd, $this->outResponse(self::SUCCESS, $chatSource->toArray())); // 心跳
                }

                // 判断有没有对应客服
                $chatUser = ChatUser::where([
                    'uid' => $user->id,
                ])->first();

                $chatUser->state = 'open';
                $chatUser->save();

                if ($chatUser->customer_id) {

                    $customer_user_fd = ApplicationContext::getContainer()->get(\Redis::class)->get(self::REDIS_CUSTOMER_KEY . $chatUser->customer_id);
                    if($customer_user_fd) {

                        if ($server->exist((int)$customer_user_fd)) {
                            $server->push((int)$customer_user_fd, $this->outResponse(self::SUCCESS, $chatSource->toArray()));
                        }

                    }else{

                        $chatUser->increment('customer_unread_num');
                        WsLog::info('customer_unread_num_01');
                        $this->queueServer->push(PushGlobalNotificationJob::class, [
                            'type' => 'customer',
                        ]);
                    }
                }else{

                    $chatUser->increment('customer_unread_num');
                    WsLog::info('customer_unread_num_02');
                    $this->queueServer->push(PushGlobalNotificationJob::class, [
                        'type' => 'customer',
                    ]);
                }
            } else {
                // 客服
                $customer = Admin::find($token['id']);

                $params = [
                    'type' => $data['type'],
                    'content' => $data['content'],
                    'img_path' => $data['img_path'],
                    'customer_id' => $customer->id,
                    'uid' => $data['uid'],
                ];

                $chat = ChatUserGroup::create($params);

                $chatSource = ChatUserGroup::with('user')->with('customer')->find($chat->id);

                if ($server->exist($frame->fd)) {
                    $server->push($frame->fd, $this->outResponse(self::SUCCESS, $chatSource->toArray())); // 心跳
                }

                // 判断有没有对应用户
                $chatUser = ChatUser::where([
                    'uid' => $data['uid'],
                ])->first();

                if ($chatUser->u_fd != 0) {
                    $chatUser->customer_id = $customer->id;
                    $chatUser->customer_unread_num = 0;
                    $chatUser->save();
                    if ($server->exist((int)$chatUser->u_fd)) {
                        $server->push((int)$chatUser->u_fd, $this->outResponse(self::SUCCESS, $chatSource->toArray()));
                    }
                } else {
                    $chatUser->customer_id = $customer->id;
                    $chatUser->save();
                    $chatUser->increment('user_unread_num');

                    // 用户不存在 发送通知
                    $this->queueServer->push(JPushSingleChatJob::class, ['uid' => $data['uid']]);
                }
            }
        }
        /*
        if($frame->opcode == Opcode::PING) {
            // 如果使用协程 Server，在判断是 PING 帧后，需要手动处理，返回 PONG 帧。
            // 异步风格 Server，可以直接通过 Swoole 配置处理，详情请见 https://wiki.swoole.com/#/websocket_server?id=open_websocket_ping_frame
            $server->push('', Opcode::PONG);
            return;
        }
        $server->push($frame->fd, 'Recv: ' . $frame->data);
        */
    }

    public function onClose($server, int $fd, int $reactorId): void
    {
        ChatUser::where('u_fd', $fd)->update([
            'is_online' => 2,
            'u_fd' => 0,
        ]);

        // 根据fd 找出客服缓存删除
        $customer_id = ApplicationContext::getContainer()->get(\Redis::class)->get(self::REDIS_CUSTOMER_FD_KEY . $fd);
        if ($customer_id) {
            ApplicationContext::getContainer()->get(\Redis::class)->del(
                self::REDIS_CUSTOMER_KEY . $customer_id,
                self::REDIS_CUSTOMER_FD_KEY . $fd
            );
        }
    }

    public function onOpen($server, $request): void
    {
        if ($request->get == null) {
            $server->push($request->fd, $this->outResponse(self::TOKEN_ERROR));
            $server->close($request->fd);
        }

        if (!is_array($request->get)) {
            $server->push($request->fd, $this->outResponse(self::TOKEN_ERROR));
            $server->close($request->fd);
        }

        if (!isset($request->get['token'])) {
            $server->push($request->fd, $this->outResponse(self::TOKEN_ERROR));
            $server->close($request->fd);
        }

        $isValid = $this->updateUserOnlineState($server, $request);

        if (!$isValid) {
            $server->close($request->fd);
        }

        // 设置上下文
        Context::set('token', base64_decode($request->get['token']));
        // $server->push($request->fd, 'Opened');
    }

    /**
     * 聊天服务统一输出.
     *
     * $message
     * $data
     * @param mixed $message
     * @param mixed $data
     */
    public function outResponse($message, $data = [])
    {
        $code = strstr($message, '_', true);
        $msg = str_replace('_', '', strstr($message, '_'));

        $responseData = [
            'code' => $code,
            'msg' => $msg,
            'data' => $data,
        ];

        return json_encode($responseData);
    }

    public function updateUserOnlineState($server, $request): bool
    {
        $data = json_decode(base64_decode($request->get['token']), true);
        if (!is_array($data)) {
            $server->push($request->fd, $this->outResponse(self::TOKEN_ERROR));
            return false;
        }

        if (!isset($data['type'])) {
            $server->push($request->fd, $this->outResponse(self::TOKEN_ERROR));
            return false;
        }
        if (!isset($data['id'])) {
            $server->push($request->fd, $this->outResponse(self::TOKEN_ERROR));
            return false;
        }

        // 普通用户
        if ($data['type'] == self::WS_TYPE_USER) {
            $user = User::find($data['id']);
            if (!$user) {
                $server->push($request->fd, $this->outResponse(self::USER_EMPTY));
                return false;
            }

            ChatUser::updateOrCreate(
                ['uid' => $data['id']],
                [
                    'uid' => $data['id'],
                    'u_fd' => $request->fd,
                    'is_online' => 1,
                ]
            );
        }

        // 客服用户
        if ($data['type'] == self::WS_TYPE_CUSTOMER_USER) {
            $admin = Admin::find($data['id']);
            if (!$admin) {
                $server->push($request->fd, $this->outResponse(self::USER_EMPTY));
                return false;
            }

            // 记录fd
            ApplicationContext::getContainer()->get(\Redis::class)->set(self::REDIS_CUSTOMER_KEY . $admin->id, $request->fd, 60 * 60 * 10);

            ApplicationContext::getContainer()->get(\Redis::class)->set(self::REDIS_CUSTOMER_FD_KEY . $request->fd, $admin->id, 60 * 60 * 10);
        }

        return true;
    }
}
